package com.ggy.ggyaiagent.agent.model;

import cn.hutool.core.collection.CollUtil;
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions;
import com.ggy.ggyaiagent.tools.*;
import jakarta.annotation.Resource;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.messages.AssistantMessage;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.messages.ToolResponseMessage;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.ChatOptions;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.model.tool.ToolCallingManager;
import org.springframework.ai.model.tool.ToolExecutionResult;
import org.springframework.ai.support.ToolCallbacks;
import org.springframework.ai.tool.ToolCallback;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 处理工具调用的基础代理类，具体实现了 think 和 act 方法，可以用做创建实例的父类
 */
@Data
@Slf4j
@EqualsAndHashCode(callSuper = true)
public class ToolCallAgent extends ReActAgent {

    private final ToolCallback[] allTools;
    //可用的工具提供者
    private final ToolCallbackProvider toolCallbackProvider;

    //保存了工具调用信息的响应
    private ChatResponse toolCallChatResponse;

    //工具调用管理者
    private final ToolCallingManager toolCallingManager;

    //禁用内部的工具调用机制，自己维护上下文。
    private final ChatOptions chatOptions;

    public ToolCallAgent(ToolCallbackProvider toolCallbackProvider, ToolCallback[] allTools) {
        super();
        this.allTools = allTools;
        this.toolCallbackProvider = toolCallbackProvider;
        this.toolCallingManager = ToolCallingManager.builder().build();
        //禁用 Spring AI 内置的工具调用机制，自己维护选项和消息上下文
        this.chatOptions = DashScopeChatOptions.builder()
                .withInternalToolExecutionEnabled(false)
                .build();
    }


    @Override
    public boolean think(SseEmitter emitter,String chatId) throws IOException {
       if(getNextStepPrompt() != null && !getNextStepPrompt().isEmpty()){
           UserMessage userMessage = new UserMessage(getNextStepPrompt());
           getMessageList().add(userMessage);
       }
        List<Message> messageList = getMessageList();
        Prompt prompt = new Prompt(messageList, chatOptions);
        try{
            //获取带工具选项的响应
            ChatResponse chatResponse = getChatClient()
                    .prompt(prompt)
                    .advisors(spec -> spec.param(ChatMemory.CONVERSATION_ID, chatId))
                    .system(getSystemPrompt())
                    .toolContext(Map.of(
                            "chatId", chatId
                    ))//将房间 id 放入上下文
//                    .toolCallbacks(allTools)
//                    .tools(new WebSearchTool(searchApiKey))
                    .toolCallbacks(allTools)
                    .toolCallbacks(toolCallbackProvider)
                    .call()
                    .chatResponse();
            //记录响应，用于 act
            this.toolCallChatResponse = chatResponse;
            AssistantMessage assistantMessage = chatResponse.getResult().getOutput();
            //输出提示信息
            String result = assistantMessage.getText();
            List<AssistantMessage.ToolCall> toolCallList = assistantMessage.getToolCalls();
            log.info(getName()+"的思考："+result);
            emitter.send(getName()+"的思考："+result);
            log.info(getName()+"选择了 "+toolCallList.size()+"个工具来使用");
            emitter.send(getName()+"选择了 "+toolCallList.size()+"个工具来使用");
            //过滤拼接参数信息
            String toolCallInfo = toolCallList.stream()
                    .map(toolCall -> String.format("工具名称：%s,参数：%s", toolCall.name(), toolCall.arguments()))
                    .collect(Collectors.joining("\n"));
             log.info(toolCallInfo);

             if(toolCallList.isEmpty()){
                 getMessageList().add(assistantMessage);
                 return false;
             }else {
                 //需要调用工具时，无需记录助手消息，因为调用工具时会自动记录
                 return true;
             }
        } catch (Exception e) {
            emitter.completeWithError(e);

            log.error(getName()+"的思考过程遇到了问题："+e.getMessage());
            getMessageList().add(
                    new AssistantMessage("处理时遇到错误："+e.getMessage())

            );
            emitter.send(getName()+"的思考过程遇到了问题："+e.getMessage());
            emitter.complete();
            return false;
        }
    }

    /**
     * 执行工具调用并处理结果
     * @return 执行结果
     */
    @Override
    public String act(SseEmitter emitter) {
        if(!toolCallChatResponse.hasToolCalls()){
            return "没有工具调用";
        }

        try {
            //调用工具
            Prompt prompt = new Prompt(getMessageList(), chatOptions);
            ToolExecutionResult toolExecutionResult = toolCallingManager.executeToolCalls(prompt, toolCallChatResponse);
            
            //记录消息上下文，conversationHistory 已经包含了助手消息和工具调用的返回结果
            setMessageList(toolExecutionResult.conversationHistory());
            
            //当前工具调用的结果
            ToolResponseMessage toolResponseMessage = (ToolResponseMessage) CollUtil.getLast(toolExecutionResult.conversationHistory());
            if (toolResponseMessage == null || toolResponseMessage.getResponses().isEmpty()) {
                log.error("工具调用没有返回结果");
                return "工具调用执行失败，没有返回结果";
            }
            
            String results = toolResponseMessage.getResponses().stream()
                    .map(response -> "工具" + response.name() + "完成了它的任务 ！ 结果：" + response.responseData())
                    .collect(Collectors.joining("\n"));

            boolean terminateToolCalled = toolResponseMessage.getResponses().stream()
                    .anyMatch(response -> "terminate".equals(response.name()));
            if(terminateToolCalled){
                setState(AgentState.FINISHED);

            }
            log.info(results);
            return results;
        } catch (Exception e) {
            log.error("执行工具调用时发生错误: " + e.getMessage(), e);
            return "执行工具调用时发生错误: " + e.getMessage();
        }
    }
}
